package logic

import (
	"bytes"
	"context"
	"crypto/rand"
	"fmt"
	"html/template"
	"log"
	"math/big"
	"os"
	"sync"

	"chat_together/app/smtp/internal/svc"
	"chat_together/app/smtp/pd"
	"chat_together/common/globalkey"

	"github.com/zeromicro/go-zero/core/logx"
	"gopkg.in/gomail.v2"
)

type GetMsgCodeLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetMsgCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMsgCodeLogic {
	return &GetMsgCodeLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

var waitGroup sync.WaitGroup

func (l *GetMsgCodeLogic) GetMsgCode(in *pd.GetMsgCodeReq) (*pd.GetMsgCodeResp, error) {
	errChan := make(chan error, 1)
	respChan := make(chan *pd.GetMsgCodeResp, 1)

	waitGroup.Add(1)

	go func() {
		defer waitGroup.Done()

		// 防止重复发送
		redisCode, err := l.svcCtx.RedisClient.GetCtx(l.ctx, fmt.Sprintf(globalkey.CacheSmtpEmailKey, in.MsgType, in.Email))
		if err != nil {
			errChan <- err
			return
		}
		if redisCode != "" {
			respChan <- &pd.GetMsgCodeResp{MsgCode: redisCode}
			return
		}

		// 生成验证码
		randCode := GetEmailCode()

		// 发送邮件
		var templatePath, subject string
		switch in.MsgType {
		case globalkey.SmtpRegister:
			subject = "CHAT TOGETHER注册验证码"
			templatePath = "D:/project_myself/chat_together_server/app/smtp/template/registerTemplate.html"
		case globalkey.SmtpLogin:
			subject = "CHAT TOGETHER登录验证码"
			templatePath = "D:/project_myself/chat_together_server/app/smtp/template/loginTemplate.html"
		case globalkey.SmtpChangePwd:
			subject = "CHAT TOGETHER重置密码验证码"
			templatePath = "D:/project_myself/chat_together_server/app/smtp/template/changepwdTemplate.html"
		default:
			errChan <- fmt.Errorf("无效的消息类型: %s", in.MsgType)
			return
		}

		emailInfo := EmailInfo{
			From:         "CHAT TOGETHER",
			To:           []string{in.Email},
			Subject:      subject,
			Template:     templatePath,
			TemplateData: map[string]interface{}{"code": randCode},
		}

		// 检查模板文件是否存在
		if _, err := os.Stat(templatePath); os.IsNotExist(err) {
			errChan <- fmt.Errorf("模板文件不存在: %s", templatePath)
			return
		}

		// 发送邮件
		if err := l.SendEmail(emailInfo); err != nil {
			errChan <- fmt.Errorf("发送邮件失败: %s", err)
			return
		}

		// 存入redis
		if err := l.svcCtx.RedisClient.SetexCtx(l.ctx, fmt.Sprintf(globalkey.CacheSmtpEmailKey, in.MsgType, in.Email), randCode, globalkey.CacheSmtpEmailKeyExp); err != nil {
			errChan <- fmt.Errorf("存入redis失败: %s", err)
			return
		}
		respChan <- &pd.GetMsgCodeResp{MsgCode: randCode}
	}()

	// 等待所有 Goroutine 结束
	go func() {
		waitGroup.Wait()
		close(errChan)
		close(respChan)
	}()

	select {
	case err := <-errChan:
		if err != nil {
			return &pd.GetMsgCodeResp{MsgCode: ""}, err
		}
	case resp := <-respChan:
		if resp != nil {
			return resp, nil
		}
	}

	return &pd.GetMsgCodeResp{MsgCode: ""}, fmt.Errorf("未知错误")
}

type EmailInfo struct {
	From         string      // 发件人
	To           []string    // 收件人
	CC           []string    // 抄送
	Subject      string      // 邮件主题
	Body         string      // 邮件正文
	Template     string      // 模板文件
	TemplateData interface{} // 模板数据
}

func (l *GetMsgCodeLogic) SendEmail(emailInfo EmailInfo) error {
	// 读取模板文件
	tmpl, err := template.ParseFiles(emailInfo.Template)
	if err != nil {
		return err
	}

	// 执行模板渲染
	var tpl bytes.Buffer
	if err := tmpl.Execute(&tpl, emailInfo.TemplateData); err != nil {
		return err
	}
	// 初始化邮件对象

	m := gomail.NewMessage()
	// m.SetHeader("From", emailInfo.From)       // 发件人
	m.SetAddressHeader("From", l.svcCtx.Config.Smtp.User, emailInfo.From)
	m.SetHeader("To", emailInfo.To...)        // 收件人
	m.SetHeader("Cc", emailInfo.CC...)        // 抄送
	m.SetHeader("Subject", emailInfo.Subject) // 邮件主题
	// m.SetBody("text/html", emailInfo.Body) // 邮件正文
	m.SetBody("text/html", tpl.String()) // 邮件正文

	// 设置 SMTP 发送邮件服务器地址和端口，以及邮箱账号和密码
	d := gomail.NewDialer(l.svcCtx.Config.Smtp.Host, l.svcCtx.Config.Smtp.Port, l.svcCtx.Config.Smtp.User, l.svcCtx.Config.Smtp.Pass)

	// 发送邮件
	if err := d.DialAndSend(m); err != nil {
		log.Println("发送邮件失败:", err)
		return err
	}
	log.Println("邮件发送成功")
	return nil
}

func GetEmailCode() string {
	const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
	const length = 6
	code := make([]byte, length)
	for i := range code {
		n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
		code[i] = charset[n.Int64()]
	}
	return string(code)
}
